package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignRecordVO;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.msg.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @description:
 * @author: ajie
 * @date: 2024/5/13 11:56:29
 * @version: 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    @Override
    public SignResultVO addSignRecords() {
        //获取当前登录用户
        Long userId = UserContext.getUser();
        //拼接key
//        SimpleDateFormat format = new SimpleDateFormat("yyyyMM");
//        format.format(new Date());
        LocalDate now = LocalDate.now();//当前时间的年月
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));//得到 冒号年月 格式字符串
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + format;

        //利用bitset命令 将签到记录保存到redis的bitmap结构中 需要校验是否已签到
        int offset = now.getDayOfMonth() - 1;//偏移量
        Boolean setbit = redisTemplate.opsForValue().setBit(key, offset, true);//返回值是原来offset上的值
        if (setbit){
            //说明当前已经签过到了
            throw new BizIllegalException("不能重复签到");
        }

        //计算连续签到的天数
        int days = countSignDays(key,now.getDayOfMonth());

        //计算连续签到 奖励积分
        int rewardPoints = 0;//代表连续签到 奖励积分
        switch (days){
            case 7:
                rewardPoints = 10;break;
            case 14:
                rewardPoints = 20;break;
            case 28:
                rewardPoints = 40;break;
        }

        //保存积分  发送消息到mq
        mqHelper.send(MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(userId,rewardPoints + 1));

        //封装vo返回
        SignResultVO vo = new SignResultVO();
        vo.setSignDays(days);
        vo.setRewardPoints(rewardPoints);
        return vo;
    }

    @Override
    public Byte[] querySignRecords() {
        //获取当前用户
        Long userId = UserContext.getUser();
        LocalDate now = LocalDate.now();//当前时间的年月
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));//得到 冒号年月 格式字符串
        //拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + format;
        int dayOfMonth = now.getDayOfMonth();

        //利用redis bitField命令取本月第一天到今天的所有签到记录
        //bitField  key  get u天数 0
        List<Long> bitField = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(bitField)){
            return new Byte[0];//0代表长度
        }
        Long num = bitField.get(0);//本月第一天到今天的签到数据 拿到的是十进制
        int offset = dayOfMonth - 1;

        //利用与运算和位移封装结果
        Byte[] arr = new Byte[dayOfMonth];
        while (offset >= 0){
            arr[offset] = (byte) (num & 1);//计算最后一天是否签到 赋值结果
            offset--;
            num = num >>> 1;//右移
        }
        return arr;
    }

    /**
     * @description: 计算连续签到的天数
     * @params: [key  缓存中的key, dayOfMonth   本月第一天到今天的天数]
     * @return:
     * @author: ajie
     * @date: 2024/5/14 9:39
     */
    private int countSignDays(String key, int dayOfMonth) {
        //求本月第一天到今天所有的签到数据 bitField 得到的是十进制
        //bitField  key  get u天数 0
        List<Long> bitField = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(bitField)){
            return 0;
        }
        Long num = bitField.get(0);//本月第一天到今天的签到数据 拿到的是十进制
        log.debug("num  {}",num);
        //num转二进制 从后往前推共多少个1  与运算& 右移1位
        int counter = 0;
        while ((num & 1) == 1){
            counter++;
            num = num>>>1;//右移一位
        }
        return counter;
    }
}
